
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <GLES/gl.h> /* For NDK*/
#include <android/log.h>
#include "utils.h"
#include "jade.h"

//#define DEBUG 1
//#define FLOAT_ARG_FAIL 1

float NORMALS_TABLE[NUMVERTEXNORMALS][3] = {
#include "anorms.h"
};


///////////////////////////////////////////////////////////////////////////////
//jade md2 model API

int jade_model_md2_get_animation_count(jade_model_md2_t *model) {

    int i, j, pos;
    int count;
    int lastId;
    char name[16], last[16];

    strcpy (last, model->m_frames[0].name);
    pos = strlen (last) - 1;
    j = 0;
    while (last[pos] >= '0' && last[pos] <= '9' && j < 2) {
        pos--;
        j++;
    }
    last[pos + 1] = '\0';

    lastId = 0;
    count = 0;

    for (i = 0; i <= model->m_header.numFrames; i++) {
        if (i == model->m_header.numFrames)
            strcpy (name, ""); // some kind of a sentinel
        else
            strcpy (name, model->m_frames[i].name);
        pos = strlen (name) - 1;
        j = 0;
        while (name[pos] >= '0' && name[pos] <= '9' && j < 2) {
            pos--;
            j++;
        }
        name[pos + 1] = '\0';

        if (strcmp (last, name)) {
            strcpy (last, name);
            count++;
        }
    }

    return count;
}


const char* 
jade_model_md2_get_animation_name (jade_model_md2_t *model, int animation) {

    int i, j, pos;
    int count;
    int lastId;
    static char last[32];
    char name[32];

    strcpy (last, model->m_frames[0].name);
    pos = strlen (last) - 1;
    j = 0;
    while (last[pos] >= '0' && last[pos] <= '9' && j < 2) {
        pos--;
        j++;
    }
    last[pos + 1] = '\0';

    lastId = 0;
    count = 0;

    for (i = 0; i <= model->m_header.numFrames; i++) {
        if (i == model->m_header.numFrames)
            strcpy (name, ""); // some kind of a sentinel
        else
            strcpy (name, model->m_frames[i].name);
        pos = strlen (name) - 1;
        j = 0;
        while (name[pos] >= '0' && name[pos] <= '9' && j < 2) {
            pos--;
            j++;
        }
        name[pos + 1] = '\0';

        if (strcmp (last, name)) {
            if (count == animation)
                return last;

            strcpy (last, name);
            count++;
        }
    }

    return 0;
}


void 
jade_model_md2_get_animation_frames (jade_model_md2_t *model, 
        int animation, unsigned int *startFrame, unsigned int *endFrame) {

    int i, j, pos;
    int count, numFrames, frameCount;
    int lastId;
    char name[16], last[16];

    strcpy (last, model->m_frames[0].name);
    pos = strlen (last) - 1;
    j = 0;
    while (last[pos] >= '0' && last[pos] <= '9' && j < 2) {
        pos--;
        j++;
    }
    last[pos + 1] = '\0';

    lastId = 0;
    count = 0;
    numFrames = 0;
    frameCount = 0;

    for (i = 0; i <= model->m_header.numFrames; i++) {
        if (i == model->m_header.numFrames)
            strcpy (name, ""); // some kind of a sentinel
        else
            strcpy (name, model->m_frames[i].name);
        pos = strlen (name) - 1;
        j = 0;
        while (name[pos] >= '0' && name[pos] <= '9' && j < 2) {
            pos--;
            j++;
        }
        name[pos + 1] = '\0';

        if (strcmp (last, name)) {
            strcpy (last, name);

            if (count == animation) {
                *startFrame = frameCount - numFrames;
                *endFrame = frameCount - 1;
                return;
            }

            count++;
            numFrames = 0;
        }
        frameCount++;
        numFrames++;
    }

    *startFrame = *endFrame = 0;
}



void jade_model_md2_set_action_index (jade_model_md2_t *model, int index) {

    if (index < model->m_actionCount) {
        model->m_actionIdx = index;
        model->m_frameIdx = model->m_actions[model->m_actionIdx].min_idx;
        model->mesh->m_enabled = GL_TRUE;
    } else {
        model->mesh->m_enabled = GL_FALSE;
        model->m_actionIdx = -1;
    }
}



jade_model_md2_t *
jade_model_md2_create (char* filename) {

    FILE *file;
    byte buffer[MD2_MAX_FRAMESIZE];
    int i;

    jade_model_md2_t *model;

    __android_log_print(ANDROID_LOG_INFO, "AndMii", "test i:%d\n", i);

    file = fopen (filename, "rb");
    if (!file)
        return NULL;

    model = malloc (sizeof (jade_model_md2_t)); 
    if (model == NULL)
        return NULL;

    /* initialize model and read header */
    fread (&model->m_header, sizeof (jade_model_md2_header_t), 1, file);

    
    LOGD ("skinWidth:\t\t%d\n", model->m_header.skinWidth);
    LOGD ("skinHeight:\t\t%d\n", model->m_header.skinHeight);

#if DEBUG
    LOGD ("magic:\t\t%d\n", model->m_header.magic);
    LOGD ("version:\t\t%d\n", model->m_header.version);
    LOGD ("skinWidth:\t\t%d\n", model->m_header.skinWidth);
    LOGD ("skinHeight:\t\t%d\n", model->m_header.skinHeight);
    LOGD ("frameSize:\t\t%d\n", model->m_header.frameSize);
    LOGD ("numSkins:\t\t%d\n", model->m_header.numSkins);
    LOGD ("numVertices:\t\t%d\n", model->m_header.numVertices);
    LOGD ("numTexCoords:\t\t%d\n", model->m_header.numTexCoords);
    LOGD ("numTriangles:\t\t%d\n", model->m_header.numTriangles);
    LOGD ("numGlCommands:\t\t%d\n", model->m_header.numGlCommands);
    LOGD ("numFrames:\t\t%d\n", model->m_header.numFrames);
    LOGD ("offsetSkins:\t\t%d\n", model->m_header.offsetSkins);
    LOGD ("offsetTexCoords:\t%d\n", model->m_header.offsetTexCoords);
    LOGD ("offsetTriangles:\t%d\n", model->m_header.offsetTriangles);
    LOGD ("offsetFrames:\t\t%d\n", model->m_header.offsetFrames);
    LOGD ("offsetGlCommands:\t%d\n", model->m_header.offsetGlCommands);
    LOGD ("offsetEnd:\t\t%d\n", model->m_header.offsetEnd);
#endif

    __android_log_print(ANDROID_LOG_INFO, "AndMii", "magic:%d\n", model->m_header.magic);

    if (model->m_header.magic != (int) (('2' << 24) + ('P' << 16) + ('D' << 8) + 'I')) {
        fclose(file);
        FREEANDNULL (model);
        return NULL;
    }

    /* read skins */
    fseek (file, model->m_header.offsetSkins, SEEK_SET);
    if (model->m_header.numSkins > 0) {

        model->m_skins = (jade_model_md2_skin_t *) malloc(sizeof(jade_model_md2_skin_t) * model->m_header.numSkins);
        if (!model->m_skins) {
            FREEANDNULL (model);
            fclose (file);
            return NULL;
        }

        for (i = 0; i < model->m_header.numSkins; i++)
            fread (&model->m_skins[i], sizeof(jade_model_md2_skin_t), 1, file);
    }

    /* read texture coordinates */
    fseek (file, model->m_header.offsetTexCoords, SEEK_SET);
    if (model->m_header.numTexCoords > 0) {
        model->m_texCoords = (jade_model_md2_textureCoordinate_t *) 
                malloc (sizeof (jade_model_md2_textureCoordinate_t) * model->m_header.numTexCoords);
        if (!model->m_texCoords) {
            FREEANDNULL (model);
            fclose (file);
            return NULL;
        }

        for (i = 0; i < model->m_header.numTexCoords; i++)
            fread (&model->m_texCoords[i], sizeof (jade_model_md2_textureCoordinate_t), 1, file);
    }

    /* read triangles */
    fseek (file, model->m_header.offsetTriangles, SEEK_SET);
    if (model->m_header.numTriangles > 0) {

        model->m_triangles = (jade_model_md2_triangle_t *) 
                malloc(sizeof(jade_model_md2_triangle_t) * model->m_header.numTriangles);

        if (!model->m_triangles) {

            FREEANDNULL (model);
            fclose (file);
            return NULL;
        }

        for (i = 0; i < model->m_header.numTriangles; i++)
            fread (&model->m_triangles[i], sizeof (jade_model_md2_triangle_t), 1, file);
    }

    /* read alias frames */
    fseek (file, model->m_header.offsetFrames, SEEK_SET);
    if (model->m_header.numFrames > 0) {
        model->m_frames = (jade_model_md2_frame_t *) 
                malloc (sizeof (jade_model_md2_frame_t) * model->m_header.numFrames);

        if (!model->m_frames) {
            FREEANDNULL (model);
            fclose (file);
            return NULL;
        }

        for (i = 0; i < model->m_header.numFrames; i++) {
            jade_model_md2_alias_frame_t *frame = (jade_model_md2_alias_frame_t *) buffer;
            int j;

            model->m_frames[i].vertices = (jade_model_md2_triangleVertex_t *) 
                malloc (sizeof (jade_model_md2_triangleVertex_t) * model->m_header.numVertices);

            if (!model->m_frames[i].vertices) {
                FREEANDNULL (model);
                fclose (file);
                return NULL;
            }

            fread (frame, 1, model->m_header.frameSize, file);
            strcpy (model->m_frames[i].name, frame->name);
            for (j = 0; j < model->m_header.numVertices; j++) {
                model->m_frames[i].vertices[j].vertex[0] = (float) ((int) frame->alias_vertices[j].vertex[0]) * frame->scale[0] + frame->translate[0];
                model->m_frames[i].vertices[j].vertex[2] = -1* ((float) ((int) frame->alias_vertices[j].vertex[1]) * frame->scale[1] + frame->translate[1]);
                model->m_frames[i].vertices[j].vertex[1] = (float) ((int) frame->alias_vertices[j].vertex[2]) * frame->scale[2] + frame->translate[2];

                model->m_frames[i].vertices[j].normal[0] = NORMALS_TABLE[frame->alias_vertices[j].lightNormalIndex][0];
                model->m_frames[i].vertices[j].normal[1] = NORMALS_TABLE[frame->alias_vertices[j].lightNormalIndex][1];
                model->m_frames[i].vertices[j].normal[2] = NORMALS_TABLE[frame->alias_vertices[j].lightNormalIndex][2];
                //model->frames[i].vertices[j].lightNormalIndex = frame->alias_vertices[j].lightNormalIndex;
/*                
                LOGD("frame[%d].vertices[%d].vertex[0]=%f, vertex[1] =%f, vertex[2] = %f\n", i, j, 
                    model->m_frames[i].vertices[j].vertex[0], 
                    model->m_frames[i].vertices[j].vertex[1],
                    model->m_frames[i].vertices[j].vertex[2]);
*/
            }
        }
    }

    //ignore gl commands reading

    fclose (file);

    model->mesh = jade_mesh_create ();
    jade_mesh_set_triangle_num (model->mesh, model->m_header.numTriangles);

    model->m_actionCount = jade_model_md2_get_animation_count (model);

#if DEBUG
    LOGD("getAnimationCount:\t%d\n", model->m_actionCount);
#endif

    model->m_actions = (jade_model_md2_action_t *) malloc(model->m_actionCount * sizeof(jade_model_md2_action_t));
    for (i = 0; i < (int) model->m_actionCount; i++) {
        model->m_actions[i].index = i;
        strcpy (model->m_actions[i].name, jade_model_md2_get_animation_name(model, i));
        jade_model_md2_get_animation_frames(model, i, &(model->m_actions[i].min_idx), &(model->m_actions[i].max_idx ));
#if DEBUG
        LOGD("m_actions[%d]:\t\t%s [%d, %d]\n", i, model->m_actions[i].name, 
                model->m_actions[i].min_idx, model->m_actions[i].max_idx);
#endif
    }

    return model;

}


void jade_model_md2_destroy (jade_model_md2_t *model) {


    FREEANDNULL (model->m_skins);
    FREEANDNULL (model->m_texCoords);
    FREEANDNULL (model->m_triangles);
    FREEANDNULL (model->m_frames);
    FREEANDNULL (model->m_actions);


    if (model->mesh != NULL)
        jade_mesh_destroy (model->mesh);


    FREEANDNULL (model);

    return;
}

void jade_model_md2_prepare_frame (jade_model_md2_t *model) {


    if (model->m_actionIdx == -1)
        return;

    float *uvs = (float *) malloc(model->m_header.numTriangles * 6 * sizeof(float));
    float *vertices = (float *) malloc(model->m_header.numTriangles * 9 * sizeof(float));
    float *normals = (float *) malloc(model->m_header.numTriangles * 9 * sizeof(float));
    int i, j, uvIdx = 0, vertexIdx = 0, normalIdx = 0;

    jade_model_md2_frame_t *f = &model->m_frames[model->m_frameIdx];

    for (i = 0; i < model->m_header.numTriangles; i++) {
        jade_model_md2_triangle_t *t = &model->m_triangles[i];
        for (j = 0; j < 3; j++) {
            //set uvs
            uvs[uvIdx++] = (float) model->m_texCoords[t->textureIndices[j]].s / (float) model->m_header.skinWidth;
            uvs[uvIdx++] = 1.0f - (float) model->m_texCoords[t->textureIndices[j]].t / (float) model->m_header.skinHeight;
            //set vertices & normals
            vertices[vertexIdx++] = f->vertices[t->vertexIndices[j]].vertex[0];
            vertices[vertexIdx++] = f->vertices[t->vertexIndices[j]].vertex[1];
            vertices[vertexIdx++] = f->vertices[t->vertexIndices[j]].vertex[2];

            normals[normalIdx++] = f->vertices[t->vertexIndices[j]].normal[0];
            normals[normalIdx++] = f->vertices[t->vertexIndices[j]].normal[2];
            normals[normalIdx++] = f->vertices[t->vertexIndices[j]].normal[1];
        }
    }

    jade_mesh_set_vertices(model->mesh, vertices, model->m_header.numTriangles * 9 * sizeof(float));
    jade_mesh_set_normals (model->mesh, normals, model->m_header.numTriangles * 9 * sizeof(float));
    jade_mesh_set_uvs (model->mesh, uvs, model->m_header.numTriangles * 6 * sizeof(float));

    FREEANDNULL(vertices);
    FREEANDNULL(normals);
    FREEANDNULL(uvs);

    model->m_frameIdx++;

    if (model->m_frameIdx > model->m_actions[model->m_actionIdx].max_idx)
        model->m_frameIdx = model->m_actions[model->m_actionIdx].min_idx;


    return;
}

void jade_model_md2_render (jade_model_md2_t *model) {

    if (model->mesh != NULL)
        jade_mesh_render (model->mesh);

    return; 
}


void jade_model_md2_set_rotate (jade_model_md2_t *model, GLfloat x, GLfloat y, GLfloat z) {

    LOGD ("mix=%d, miy=%d, miz=%d\n", x, y, z);
    //LOGD ("mfx=%d, mfy=%d, mfz=%d\n", x, y, z);
    //LOGD ("gx=%f, gy=%f, gz=%f\n", (GLfloat)x, (GLfloat)y, (GLfloat)z);
    //setRotate ((GLfloat)10.0f, (GLfloat)10.0f, (GLfloat)10.0f);
    //setRotate ((float)x, (float)y, (float)z);
    jade_mesh_set_rotate (model->mesh, x, y, z);
    //setRotate (x, y, z);
    //LOGD ("mfx=%f, mfy=%f, mfz=%f\n", (float)x, (float)y, (float)z);

}

void jade_model_md2_set_texture_offset(jade_model_md2_t *model, GLfloat x, GLfloat y, GLfloat z) {

    LOGD ("jade_model_md2_set_texture_offset: mix=%d, miy=%d, miz=%d\n", x, y, z);
    jade_mesh_set_texture_offset (model->mesh, x, y, z);
}

void jade_model_md2_set_texture_id (jade_model_md2_t *model, GLint textureId) {

    jade_mesh_set_texture_id (model->mesh, textureId);

    return;

}

void jade_model_md2_set_scale (jade_model_md2_t *model, GLfloat x, GLfloat y, GLfloat z) {

    jade_mesh_set_scale (model->mesh, x, y, z);

}

void jade_model_md2_set_position (jade_model_md2_t *model, GLfloat x, GLfloat y, GLfloat z) {

    jade_mesh_set_position (model->mesh, x, y, z);

}


